﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HSDc.RingleFlow.Kernel.Persistence;
using HSDc.RingleFlow.Api.Kernel;
using HSDc.RingleFlow.Kernel.DBPersit.Dao.Util;
using HSDc.RingleFlow.Kernel.DBPersit.PersitEnum;

namespace HSDc.RingleFlow.Kernel.DBPersit.Dao
{
    internal class DBProcessInstance : WFPersitable
    {
        void WFPersitable.Save(object persistObject)
        {
            IProcessInstance instance = (IProcessInstance)persistObject;
            RingleFlowEntities entity = ConnectionFactory.GetInstance().GetEntities();
            ProcessInstanceDto dto = RetrieveDto(instance.Id, entity);
            dto = SaveProcessInstance(instance, entity, dto);
            AddActvitiyInstance(entity, dto.ProcessInstance_ID, ((ProcessInstance)instance).GetCurrentTasks());
            entity.SaveChanges(System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
        }

        private static ProcessInstanceDto SaveProcessInstance(IProcessInstance instance, RingleFlowEntities entity, ProcessInstanceDto dto)
        {
            return ProcessInstanceDao.SaveProcessInstance(instance, entity, dto);
        }

        private ProcessInstanceDto RetrieveDto(string id, RingleFlowEntities entity)
        {
            return ProcessInstanceDao.RetrieveDto(id, entity);
        }

        #region Save Activity Instance
        private void AddActvitiyInstance(RingleFlowEntities entity, int instanceId, List<ActivityInstance> newActivity)
        {
            ActivityInstanceDto dto = null;
            foreach (ActivityInstance ai in newActivity)
            {
                if (!ActivityInstanceExist(entity, ai))
                {
                    dto = ActivityInstanceDto.CreateActivityInstanceDto(0, ai.Id, DateTime.Now, instanceId,
                       RetrieveActivityDefinitionId(entity, ai.ActivityDefinitionId));
                    entity.AddToActivityInstanceDto(dto);
                }
                else
                {
                    dto = RetrieveActivityInstance(entity, ai.Id);
                }
                FillActivityInstanceDtoValue(ai, ref dto);
            }
        }

        private int RetrieveActivityDefinitionId(RingleFlowEntities entity, string definitionId)
        {
            var activity = from pad in entity.ProcessActivityDto
                           where (pad.Activity_GUID.ToLower() == definitionId.ToLower())
                           select pad.Activity_ID;
            return activity.First();
        }

        private bool ActivityInstanceExist(RingleFlowEntities entity, ActivityInstance ai)
        {
            var acCount = from ait in entity.ActivityInstanceDto
                          where (ait.ActivityInstance_GUID == ai.Id)
                          select ait.ActivityInstance_ID;
            return (acCount.ToList().Count > 0);
        }

        private ActivityInstanceDto RetrieveActivityInstance(RingleFlowEntities entity, string guid)
        {
            var ac = (from ait in entity.ActivityInstanceDto
                          where (ait.ActivityInstance_GUID == guid)
                          select ait).Single();
            return ac;
        }

        private void FillActivityInstanceDtoValue(ActivityInstance instance, ref ActivityInstanceDto dto)
        {
            dto.ActivityInstance_Status = (int)((ITask)instance).CurrentState;
            dto.ActivityInstance_Assignee = ((ITask)instance).Assignee;
            if (((ITask)instance).CurrentState == TaskState.Completed)
                dto.ActivityInstance_End = DateTime.Now;
        }
        #endregion
    }
    internal class DBCompleteProcessInstance : WFPersitable
    {
        void WFPersitable.Save(object persistObject)
        {
            IProcessInstance instance = (IProcessInstance)persistObject;
            RingleFlowEntities entity = ConnectionFactory.GetInstance().GetEntities();
            ProcessInstanceDto dto = RetrieveDto(instance.Id, entity);
            dto = ProcessInstanceDao.SaveProcessInstance(instance, entity, dto);
            MoveToHistory(dto, entity);
            entity.SaveChanges(System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
        }

        private ProcessInstanceDto RetrieveDto(string id, RingleFlowEntities entity)
        {
            return ProcessInstanceDao.RetrieveDto(id, entity);
        }

        private void MoveToHistory(ProcessInstanceDto dto, RingleFlowEntities entity)
        {
            // 先搬ProcessInstance到ProcessInstanceHistory
            int pihId = CopyProcessInstanceToHistory(dto, entity);
            // 再搬ActivityInstance到ActivityInstanceHistory
            MoveActivityInstanceToHistory(dto, entity, pihId);
            // 再搬InstanceVariable(for ProcessInstance)到History
            MoveProcessInstanceVariableToHistory(entity, dto.ProcessInstance_GUID);
            // 刪除ProcessInstanceDto
            entity.DeleteObject(dto);
        }

        private void MoveProcessInstanceVariableToHistory(RingleFlowEntities entity, string guid)
        {
            var varList = from variable in entity.InstanceVariableDto
                          where ((variable.Instance_GUID.ToLower() == guid.ToLower()) &&
                                     (variable.Variable_Type == (int)VariableType.ProcessInstance))
                          select variable;
            MoveInstanceVariables(entity, varList);
        }

        private static void MoveInstanceVariables(RingleFlowEntities entity, IQueryable<InstanceVariableDto> varList)
        {
            foreach (InstanceVariableDto ivDto in varList)
            {
                InstanceVariableHistoryDto ivhDto = InstanceVariableHistoryDto.CreateInstanceVariableHistoryDto(
                    0, ivDto.Variable_Name, ivDto.Instance_GUID);
                ivhDto.Variable_Type = ivDto.Variable_Type;
                ivhDto.Variable_Value = ivDto.Variable_Value;
                entity.AddToInstanceVariableHistoryDto(ivhDto);
                entity.DeleteObject(ivDto);
            }
        }

        private void MoveActivityInstanceToHistory(ProcessInstanceDto dto, RingleFlowEntities entity, int pihId)
        {
            var aiList = from ai in entity.ActivityInstanceDto
                         where (ai.ActivityInstance_Belong_ProcessInstance == dto.ProcessInstance_ID)
                         select ai;
            foreach (ActivityInstanceDto aiDto in aiList)
            {
                MoveActivityInstanceVariableToHistory(entity, aiDto);
                ActivityInstanceHistoryDto aihDto = ActivityInstanceHistoryDto.CreateActivityInstanceHistoryDto(
                    0, aiDto.ActivityInstance_GUID, aiDto.ActivityInstance_Start, pihId, aiDto.ActivityInstance_Belong_Actvity);
                aihDto.ActivityInstance_Assignee = aiDto.ActivityInstance_Assignee;
                aihDto.ActivityInstance_End = aiDto.ActivityInstance_End;
                aihDto.ActivityInstance_Status = aiDto.ActivityInstance_Status;
                aihDto.ActivityInstance_Type = aihDto.ActivityInstance_Type;
                entity.AddToActivityInstanceHistoryDto(aihDto);
                entity.DeleteObject(aiDto);
            }
        }

        private void MoveActivityInstanceVariableToHistory(RingleFlowEntities entity, ActivityInstanceDto aiDto)
        {
            var varList = from variable in entity.InstanceVariableDto
                          where ((variable.Instance_GUID.ToLower() == aiDto.ActivityInstance_GUID.ToLower()) &&
                                     (variable.Variable_Type == (int)VariableType.ActivityInstance))
                          select variable;
            MoveInstanceVariables(entity, varList);
        }

        private int CopyProcessInstanceToHistory(ProcessInstanceDto dto, RingleFlowEntities entity)
        {
            ProcessInstanceHistoryDto pihDto = ProcessInstanceHistoryDto.CreateProcessInstanceHistoryDto(0, dto.ProcessInstance_GUID,
                dto.ProcessInstance_Start, dto.ProcessInstance_Belong_Definition);
            pihDto.ProcessInstance_End = dto.ProcessInstance_End;
            pihDto.ProcessInstance_Status = dto.ProcessInstance_Status;
            entity.AddToProcessInstanceHistoryDto(pihDto);
            return pihDto.ProcessInstance_ID;
        }
    }

    internal class DBAutoCompleteActivityIntance : WFPersitable
    {
        void WFPersitable.Save(object persistObject)
        {
            throw new NotImplementedException();
        }
    }

    internal class ProcessInstanceDao
    {
        internal static ProcessInstanceDto RetrieveDto(string id, RingleFlowEntities entity)
        {
            var dto = (from pi in entity.ProcessInstanceDto
                       where (pi.ProcessInstance_GUID.ToLower() == id.ToLower())
                       select pi).Single();
            return dto;
        }

        internal static ProcessInstanceDto SaveProcessInstance(IProcessInstance instance, RingleFlowEntities entity, ProcessInstanceDto dto)
        {
            RuntimeServiceDao.FillProcessInstanceDtoValue((ProcessInstance)instance, ref dto);
            Dictionary<string, object> variables = instance.GetVaraibles();
            if ((variables != null) && (variables.Count > 0))
                RuntimeServiceDao.SaveProcessInstanceVariables(variables, instance.Id, entity);
            return dto;
        }
    }

    internal class ActivityInstanceDao
    {
    }
}
